package raft

import (
	"sort"
	"time"
)

func (rf *Raft) heartBeats(term int, leaderId int, leaderCommit int) {
	defer rf.persist()
	for i, _ := range rf.peers {
		if i == leaderId {
			continue
		}
		go func(i int) {
			// 判断nextIndex[i] 是否已经存入快照了 是则发送InstallSnapshotRPC 否则发送AppendEntriesRPC
			rf.mu.Lock()
			if rf.nextIndex[i] > rf.lastIncludedIndex {
				args := AppendEntriesRequest{
					Term:         term,
					LeaderId:     leaderId,
					LeaderCommit: leaderCommit,
				}
				reply := AppendEntriesResponse{}
				// 找到上个匹配日志的任期号和日志号，因为可能日志已经被删除了，所以应该初始化为lastIncludedIndex
				if rf.nextIndex[i]-1 == rf.lastIncludedIndex {
					args.PrevLogIndex = rf.lastIncludedIndex
					args.PrevLogTerm = rf.lastIncludedTerm
					args.Entries = rf.logs
				} else {
					for idx, log := range rf.logs {
						if log.LogIndex == rf.nextIndex[i]-1 {
							args.PrevLogIndex = log.LogIndex
							args.PrevLogTerm = log.Term
							args.Entries = rf.logs[idx+1:]
							break
						}
					}
				}

				newPreIndex, _ := rf.getLastLogIndexAndTerm()
				rf.mu.Unlock()
				ok := rf.peers[i].Call("Raft.AppendEntries", &args, &reply)
				rf.mu.Lock()
				if ok {
					if reply.Term > term {
						rf.term = args.Term
						rf.votedFor = -1
						if rf.state != 0 {
							rf.changeState <- 0
						}
					} else {
						if reply.Success {
							DPrintf("change matchidx from %v to %v", rf.matchIndex[i], newPreIndex)
							rf.matchIndex[i] = max(rf.matchIndex[i], newPreIndex)
							rf.nextIndex[i] = rf.matchIndex[i] + 1
						} else {
							//If AppendEntries fails because of log inconsistency:
							//decrement nextIndex and retry (§5.3)
							rf.nextIndex[i] = max(1, args.PrevLogIndex-10)
						}
					}
				}

				rf.mu.Unlock()
			} else {
				// 发送快照
				//DPrintf("发送InstallSnapshotRPC")
				args := InstallSnapshotRequest{
					Term:             term,
					LeaderID:         leaderId,
					LastIncludeIndex: rf.lastIncludedIndex,
					LastIncludeTerm:  rf.lastIncludedTerm,
					Data:             rf.persister.ReadSnapshot(),
				}
				rf.mu.Unlock()
				reply := InstallSnapshotResponse{}
				ok := rf.peers[i].Call("Raft.InstallSnapshot", &args, &reply)
				rf.mu.Lock()
				if ok {
					if reply.Term > term {
						rf.term = args.Term
						rf.votedFor = -1
						if rf.state != 0 {
							rf.changeState <- 0
						}
						return
					} else {
						rf.nextIndex[i] = args.LastIncludeIndex + 1
						rf.matchIndex[i] = args.LastIncludeIndex
					}
				}
				rf.mu.Unlock()
			}
		}(i)
	}
	time.Sleep(time.Millisecond * 20)
	Index := make([]int, len(rf.peers))
	rf.mu.Lock()
	copy(Index, rf.matchIndex)
	lastIdx, _ := rf.getLastLogIndexAndTerm()
	Index[leaderId] = lastIdx
	sort.Ints(Index)
	if cmt := Index[(len(rf.peers))/2]; cmt > rf.commitIndex {
		rf.commitIndex = cmt
	}
	rf.mu.Unlock()
}
